﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
//using System.Windows.Shapes;
using Coding4Fun;
using Coding4Fun.Kinect.Wpf;
using Microsoft.Kinect;
using Kinect.Toolbox;
using Kinect.Toolbox.Record;
using Microsoft.Speech.Recognition;
using Microsoft.Win32;
using System.IO;
using System.Collections;
using System.Diagnostics;
using Deb.Drivers;
using System.Threading;
using System.Windows.Threading;
using Kinect.Speech;
using Mapper;

namespace KinaestheticDebuggerEnvironment
{
    /// <summary>
    /// Interaction logic for Page2.xaml
    /// </summary>
    public partial class Page2 : Page
    {
        KinectSensor kinectSensor;

        SwipeGestureDetector swipeGestureRecognizer;
        TemplatedGestureDetector circleGestureRecognizer;
        SkeletonDisplayManager skeletonDisplayManager;
        readonly BarycenterHelper barycenterHelper = new BarycenterHelper();
        AlgorithmicPostureDetector algorithmicPostureRecognizer;
        TemplatedPostureDetector templatePostureDetector;
        private bool recordNextFrameForPosture;

        private DispatcherTimer readyTimer;
        private DispatcherTimer debugModeTimer;
        private static SpeechRecognitionEngine speechRecognizer;
        SpeechRecognizer speechRec = new SpeechRecognizer(speechRecognizer);

        string circleKBPath;
        string letterT_KBPath;

        SkeletonRecorder recorder;

        private Skeleton[] skeletons;

        VSDriver vs = new VSDriver();
        MappingUnit map = new MappingUnit();

        EnvDTE.DTE ide;

        private string selectedIDE;
        private bool gestureRecognitionStatus;
        private bool speechRecognitionStatus;

        private string oldDebugMode = System.String.Empty;

        //Gesture images
        Image go_image = new Image();
        BitmapImage src_go_image = new BitmapImage();           
        Image break_image = new Image();
        BitmapImage src_break_image = new BitmapImage();
        Image stop_image = new Image();
        BitmapImage src_stop_image = new BitmapImage();
        Image stepInto_image = new Image();
        BitmapImage src_stepInto_image = new BitmapImage();
        Image stepOut_image = new Image();
        BitmapImage src_stepOut_image = new BitmapImage();
        Image stepOver_image = new Image();
        BitmapImage src_stepOver_image = new BitmapImage();

        public Page2()
        {
            InitializeComponent();
        }

        public Page2(string selectedIDE, bool gestureRecognitionStatus, bool speechRecognitionStatus):this()
        {
            this.selectedIDE = selectedIDE;
            this.gestureRecognitionStatus = gestureRecognitionStatus;
            this.speechRecognitionStatus = speechRecognitionStatus; 
        }

        void Kinects_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            switch (e.Status)
            {
                case KinectStatus.Connected:
                    if (kinectSensor == null)
                    {
                        kinectSensor = e.Sensor;
                        Initialize();
                    }
                    break;
                case KinectStatus.Disconnected:
                    if (kinectSensor == e.Sensor)
                    {
                        Clean();
                        MessageBox.Show("Kinect was disconnected");
                    }
                    break;
                case KinectStatus.NotReady:
                    break;
                case KinectStatus.NotPowered:
                    if (kinectSensor == e.Sensor)
                    {
                        Clean();
                        MessageBox.Show("Kinect is no more powered");
                    }
                    break;
                default:
                    MessageBox.Show("Unhandled Status: " + e.Status);
                    break;
            }
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            circleKBPath = Path.Combine(Environment.CurrentDirectory, @"data\circleKB.save");
            letterT_KBPath = Path.Combine(Environment.CurrentDirectory, @"data\t_KB.save");

            try
            {
                //listen to any status change for Kinects
                KinectSensor.KinectSensors.StatusChanged += Kinects_StatusChanged;

                //loop through all the Kinects attached to this PC, and start the first that is connected without an error.
                foreach (KinectSensor kinect in KinectSensor.KinectSensors)
                {
                    if (kinect.Status == KinectStatus.Connected)
                    {
                        kinectSensor = kinect;
                        break;
                    }
                }

                if (KinectSensor.KinectSensors.Count == 0)
                    MessageBox.Show("No Kinect found");
                else
                {
                    Initialize();
                    
                }
                
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void Initialize()
        {
            if (kinectSensor == null)
                return;

            //Initialize connection with the selected IDE
            InitializeIDEConnection();

            if (gestureRecognitionStatus && speechRecognitionStatus)
            {
                //Initialize gesture recognition engine
                InitializeKinectGesture();
                //Initialize speech recognition engine
                InitializeKinectSpeech();
                this.WindowHeight = 740;
            }
            else if (gestureRecognitionStatus)
            {
                //Initialize gesture recognition engine
                InitializeKinectGesture();
                //
                spPage2.Children.Remove(gbSpeechPanel);
                this.WindowHeight = 630;
            }
            else if (speechRecognitionStatus)
            {
                //Initialize speech recognition engine
                InitializeKinectSpeech();
                //
                spPage2.Children.Remove(gbSkeletonViewer);
                spPage2.Children.Remove(gbGesturePanel);
                this.WindowHeight = 330;
                this.WindowWidth = 340;
            }

            //Start Kinect sensor
            kinectSensor.Start();

            //Setup debugModeTimer 
            debugModeTimer = new System.Windows.Threading.DispatcherTimer();
            debugModeTimer.Tick += new EventHandler(debugModeTimer_Tick);
            debugModeTimer.Interval = new TimeSpan(0, 0, 1);
            debugModeTimer.Start();

        }
        private void debugModeTimer_Tick(object sender, EventArgs e)
        {
            string currentDebugMode = vs.GetDebuggerMode(ide);
            if (!currentDebugMode.Equals(oldDebugMode))
            {
                OnDebugModeChanged(currentDebugMode);
            }
            
        }

        private void OnDebugModeChanged(string debugMode)
        {
            // Updating the Label which displays the current mode
            debuggerStatusLabel.Content = debugMode;
            //set oldDebugMode to the current mode
            oldDebugMode = debugMode;

            //clear stackpanel in every mode change
            spSuggestions.Children.Clear();

            switch (debugMode)
            {
                case "dbgDesignMode":
                    tbSuggestions.Text = "Say: Start";
                    spSuggestions.Children.Add(go_image);
                    break;
                case "dbgRunMode":
                    tbSuggestions.Text = "Say: Stop, Break";
                    spSuggestions.Children.Add(stop_image);
                    spSuggestions.Children.Add(break_image);
                    break;
                case "dbgBreakMode":
                    tbSuggestions.Text = "Say: Start, Stop, Step Into, Step Out, Step Over";
                    spSuggestions.Children.Add(go_image);
                    spSuggestions.Children.Add(stop_image);
                    spSuggestions.Children.Add(stepInto_image);
                    spSuggestions.Children.Add(stepOut_image);
                    spSuggestions.Children.Add(stepOver_image);
                    break;
            }
            
        }

        void InitializeIDEConnection()
        {
            
            //select IDE ID and Name
            string[] selectedIDETokenized = selectedIDE.Split(new Char[] { ':' });
            string solutionName = selectedIDETokenized[2];
            string solutionID = selectedIDETokenized[0] + ":" + selectedIDETokenized[1];

            this.connectedIDELabel.Text = solutionName;

            //Get IDE Instance
            try
            {
                ide = vs.GetIDEInstance(solutionID);
                //debuggerStatusLabel.Content = vs.GetDebuggerMode(ide);
            }
            catch(Exception ex)
            {
                Debug.WriteLine("Exception getting IDE instance: {0}", ex);
            }
            
        }

        void InitializeKinectGesture()
        {
            kinectSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);

            kinectSensor.SkeletonStream.Enable(new TransformSmoothParameters
            {
                Smoothing = 0.5f,
                Correction = 0.5f,
                Prediction = 0.5f,
                JitterRadius = 0.05f,
                MaxDeviationRadius = 0.04f
            });
            kinectSensor.SkeletonFrameReady += kinectRuntime_SkeletonFrameReady;

            swipeGestureRecognizer = new SwipeGestureDetector();
            swipeGestureRecognizer.OnGestureDetected += OnGestureDetected;

            algorithmicPostureRecognizer = new AlgorithmicPostureDetector();
            algorithmicPostureRecognizer.PostureDetected += PostureDetected;

            skeletonDisplayManager = new SkeletonDisplayManager(kinectSensor, kinectCanvas);

            LoadCircleGestureDetector();
            LoadLetterTPostureDetector();

            //Load suggested gesture images
            src_go_image.BeginInit();
            src_go_image.UriSource = new Uri("Images\\RightHello.jpg", UriKind.Relative);
            src_go_image.EndInit();
            go_image.Source = src_go_image;
            go_image.Stretch = Stretch.Uniform;

            src_break_image.BeginInit();
            src_break_image.UriSource = new Uri("Images\\LeftHello.jpg", UriKind.Relative);
            src_break_image.EndInit();
            break_image.Source = src_break_image;
            break_image.Stretch = Stretch.Uniform;

            src_stop_image.BeginInit();
            src_stop_image.UriSource = new Uri("Images\\JointHands.jpg", UriKind.Relative);
            src_stop_image.EndInit();
            stop_image.Source = src_stop_image;
            stop_image.Stretch = Stretch.Uniform;

            src_stepInto_image.BeginInit();
            src_stepInto_image.UriSource = new Uri("Images\\SwipeToRight.jpg", UriKind.Relative);
            src_stepInto_image.EndInit();
            stepInto_image.Source = src_stepInto_image;
            stepInto_image.Stretch = Stretch.Uniform;

            src_stepOut_image.BeginInit();
            src_stepOut_image.UriSource = new Uri("Images\\SwipeToLeft.jpg", UriKind.Relative);
            src_stepOut_image.EndInit();
            stepOut_image.Source = src_stepOut_image;
            stepOut_image.Stretch = Stretch.Uniform;

            src_stepOver_image.BeginInit();
            src_stepOver_image.UriSource = new Uri("Images\\Circle.jpg", UriKind.Relative);
            src_stepOver_image.EndInit();
            stepOver_image.Source = src_stepOver_image;
            stepOver_image.Stretch = Stretch.Uniform;
  
        }

        void kinectRuntime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            using (SkeletonFrame frame = e.OpenSkeletonFrame())
            {
                if (frame == null)
                    return;

                if (recorder != null)
                    recorder.Record(frame);

                Tools.GetSkeletons(frame, ref skeletons);

                if (skeletons.All(s => s.TrackingState == SkeletonTrackingState.NotTracked))
                    return;

                ProcessFrame(frame);
                //debuggerStatusLabel.Content = vs.GetDebuggerMode(ide);
            }
        }

        void ProcessFrame(ReplaySkeletonFrame frame)
        {
            foreach (var skeleton in frame.Skeletons)
            {
                if (skeleton.TrackingState != SkeletonTrackingState.Tracked)
                    continue;

                barycenterHelper.Add(skeleton.Position.ToVector3(), skeleton.TrackingId);
                if (!barycenterHelper.IsStable(skeleton.TrackingId))
                    return;

                foreach (Joint joint in skeleton.Joints)
                {
                    if (joint.TrackingState != JointTrackingState.Tracked)
                        continue;

                    if (joint.JointType == JointType.HandRight)
                    {
                        swipeGestureRecognizer.Add(joint.Position, kinectSensor, joint.JointType);
                        circleGestureRecognizer.Add(joint.Position, kinectSensor, joint.JointType);
                    }
                    
                }

                algorithmicPostureRecognizer.TrackPostures(skeleton);
                templatePostureDetector.TrackPostures(skeleton);

                if (recordNextFrameForPosture)
                {
                    templatePostureDetector.AddTemplate(skeleton);
                    recordNextFrameForPosture = false;
                }
            }

            skeletonDisplayManager.Draw(frame);
        }

        private void Page_Unloaded(object sender, RoutedEventArgs e)
        {
            Clean();
        }

        private void Clean()
        {
            if (swipeGestureRecognizer != null)
            {
                swipeGestureRecognizer.OnGestureDetected -= OnGestureDetected;
            }

            if (algorithmicPostureRecognizer != null)
            {
                algorithmicPostureRecognizer.PostureDetected -= PostureDetected;
            }

            if (speechRecognizer != null)
            {
                speechRec.OnSpeechDetected -= OnSpeechDetected;
                speechRec.OnSpeechAccepted -= OnSpeechAccepted;
                speechRec.OnSpeechTilt -= OnSpeechTilt;
            }

            CloseGestureDetector();

            ClosePostureDetector();

            if (recorder != null)
            {
                recorder.Stop();
                recorder = null;
            }

            if (kinectSensor != null)
            {
                kinectSensor.SkeletonFrameReady -= kinectRuntime_SkeletonFrameReady;
                kinectSensor.Stop();
                kinectSensor = null;
            }
            
        }

        //Control the Kinect tilt control through the slider
        private void slider1_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            //Set angle to slider1 value
            if (kinectSensor != null && kinectSensor.IsRunning)
            {
                kinectSensor.ElevationAngle = (int)slider1.Value;
                //lblCurrentAngle.Content = kinectSensor.ElevationAngle;
            }

            //Do not change Elevation Angle often, please see documentation on this and Kinect Explorer for a robust example
            System.Threading.Thread.Sleep(new TimeSpan(hours: 0, minutes: 0, seconds: 1));
        }
             
    }
    
}
